package org.jsets.fastboot.dsc.service.impl;

import org.jsets.fastboot.database.JdbcUtils;
import org.jsets.fastboot.dsc.mapper.DataSourceConfigMapper;
import org.jsets.fastboot.dsc.model.dto.DataSourceConfigQuery;
import org.jsets.fastboot.dsc.model.entity.DataSourceConfig;
import org.jsets.fastboot.dsc.service.IDataSourceConfigService;
import org.jsets.fastboot.persistent.support.LambdaQuerier;
import org.jsets.fastboot.util.IoUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import javax.sql.DataSource;

/**
 * 行政区划 <br>
 *
 * @author wj596
 */
@Slf4j
@Service
public class DataSourceConfigService extends ServiceImpl<DataSourceConfigMapper, DataSourceConfig> implements IDataSourceConfigService {

	private static final String CACHE_NAME = "dsc_data_source_config";

	private static final Map<Long, DataSource> instances = Maps.newConcurrentMap();
	private static final Object monitor = new Object();

	public Optional<DataSourceConfig> findByName(String name){
		return LambdaQuerier.build(DataSourceConfig.class).setBaseMapper(this.getBaseMapper()).eq(DataSourceConfig::getName, name).findOne();
	}
	
	@Override
	@Cacheable(value = CACHE_NAME, key = "#p0", unless = "#result == null")
	public DataSourceConfig getEntity(Long id) {
		return LambdaQuerier
				.build(DataSourceConfig.class)
				.setBaseMapper(this.getBaseMapper())
				.eq(DataSourceConfig::getId, id).selectOne();
	}

	@Override
	@CacheEvict(value = CACHE_NAME, key = "#p0.id")
	public int updateEntity(DataSourceConfig entity) {
		this.removeInstance(entity.getId());
		return this.baseMapper.updateById(entity);
	}

	@Override
	@CacheEvict(value = CACHE_NAME, key = "#p0.id")
	public int deleteEntity(Long id) {
		this.removeInstance(id);
		return this.baseMapper.deleteById(id);
	}

	@Override
	public Page<DataSourceConfig> selectPage(DataSourceConfigQuery criteria) {
		return LambdaQuerier
				.build(DataSourceConfig.class)
				.setBaseMapper(this.getBaseMapper())
				.setCurrentPage(criteria.getCurrent())
				.setPageSize(criteria.getSize())
				.likeIfNecessary(DataSourceConfig::getUrl, criteria.getUrl())
				.likeIfNecessary(DataSourceConfig::getName, criteria.getName())
				.eqIfNecessary(DataSourceConfig::getDriverType, criteria.getDriverType())
				.selectPage();
	}

	@Override
	public List<DataSourceConfig> selectList(DataSourceConfigQuery criteria) {
		return LambdaQuerier
				.build(DataSourceConfig.class)
				.setBaseMapper(this.getBaseMapper())
				.setCurrentPage(criteria.getCurrent())
				.setPageSize(criteria.getSize())
				.likeIfNecessary(DataSourceConfig::getUrl, criteria.getUrl())
				.likeIfNecessary(DataSourceConfig::getName, criteria.getName())
				.eqIfNecessary(DataSourceConfig::getDriverType, criteria.getDriverType())
				.selectList();
	}

	@Override
	public void testing(DataSourceConfig entity) {
		Connection conn = null;
		try {
			Class.forName(JdbcUtils.getDriverClassName(entity.getDriverType()));
			conn = DriverManager.getConnection(entity.getUrl(), entity.getUsername(), entity.getPassword());
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		} finally {
			IoUtils.closeQuietly(conn);
		}
	}

	@Override
	public DataSource getInstance(Long id) {
		if (instances.containsKey(id)) {
			return instances.get(id);
		}

		DataSourceConfig config = this.getById(id);
		Objects.requireNonNull(config, "找不到主键为[" + id + "]的数据源");

		synchronized (monitor) {
			if (instances.containsKey(config.getId())) {
				return instances.get(config.getId());
			}
			DruidDataSource datasource = new DruidDataSource();
			datasource.setDriverClassName(JdbcUtils.getDriverClassName(config.getDriverType()));
			datasource.setUrl(config.getUrl());
			datasource.setUsername(config.getUsername());
			datasource.setPassword(config.getPassword());
			log.info("创建数据源[{}]", config.getUrl());
			instances.put(config.getId(), datasource);
			return datasource;
		}
	}

	@Override
	public Connection getConnection(Long id) {
		DataSourceConfig config = this.getById(id);
		Objects.requireNonNull(config, "找不到主键为[" + id + "]的数据源");

		Connection conn = null;
		try {
			Class.forName(JdbcUtils.getDriverClassName(config.getDriverType()));
			conn = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
			return conn;
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	private boolean removeInstance(Long id) {
		if (!instances.containsKey(id)) {
			return false;
		}

		boolean doIt = false;
		synchronized (monitor) {
			if (instances.containsKey(id)) {
				instances.remove(id);
				doIt = true;
			}
		}
		return doIt;
	}
}